package com.solarStruggle.graphics

import java.awt.{Color}
import util.{Random}

class DiamondSquare(squareSize: Int, colorSpec: ColorSpec, baseImage: BufferedImage)
extends ProcessedImageGenerator(baseImage) {
  
  private val r = new Random()
  
  override def process(image: BufferedImage) {
    diamondSquare(image, squareSize, squareSize)
  }
  
  /**
   * Performas the diamond-square algorithm with the given square size
   */
   def diamondSquare(image: BufferedImage, width: Int, height: Int) {
     
     for(x <- 0 until image.getWidth / width; y <- 0 until image.getHeight / height) {
       diamond(image, x * width, y * height, width, height)
       square(image, x * width, y * height, width, height)
     }
     
     if(width > 2 && height > 2)
       diamondSquare(image, width / 2, height / 2)
  }
  
  /**
   * The "diamond" portion of the diamond-squares algorithm.  Divides the image
   * into a set of squares, and fills the pixel in the center of each square
   * by averaging the corners and adding a random value
   */
  def diamond(image: BufferedImage, x: Int, y: Int, width: Int, height: Int) {
    val avg = averageColors(image.getPixel(x        , y         ) ::
                            image.getPixel(x + width, y + height) ::
                            image.getPixel(x + width, y         ) ::
                            image.getPixel(x        , y + height) :: Nil)
    image.setPixel(x + width / 2, y + height / 2, getRandomColor(avg, colorSpec))
  }
  
  /**
   * The "square" portion of the diamond-square algorithm.  For each square,
   * fills in the pixels on the center of each edge by averaging the pixels
   * in a diamond surrounding them.
   */
  def square(bi: BufferedImage, x: Int, y: Int, width: Int, height: Int) {
    /**
     * Gets the average color of a diamond surrounding a point
     */
    def getDiamondColor(x: Int, y: Int): Color =
      averageColors(bi.getPixel(x - width / 2, y             ) ::
                    bi.getPixel(x            , y - height / 2) ::
                    bi.getPixel(x + width / 2, y             ) :: 
                    bi.getPixel(x            , y + height / 2) :: Nil)
    
    /**
     * Sets the diamond at a centerpoint
     */
    def doDiamond(x: Int, y: Int) {
      val avgColor = getDiamondColor(x, y)
      bi.setPixel(x, y, getRandomColor(avgColor, colorSpec))
    }
       
    
    doDiamond(x            , y + height / 2)
    doDiamond(x + width / 2, y             )
    doDiamond(x + width    , y + height / 2)
    doDiamond(x + width / 2, y + height    )
  }
  
  /**
   * Computes a new color by averaging each of the colors in the given sequence
   */
  def averageColors(colors: Seq[Color]): Color = {
    /**
     * Averages a single channel of a color
     */
    def avgColorChannel(colors: Seq[Color], channelFn: Color => int) =
      (colors.foldLeft (0) { (x, c) => x + channelFn(c) }) / Math.max(1, colors.length)
 
      new Color(avgColorChannel(colors, { c => c.getRed }),
                avgColorChannel(colors, { c => c.getGreen }),
                avgColorChannel(colors, { c => c.getBlue }),
                avgColorChannel(colors, { c => c.getAlpha }))
  }
  
  /**
   * Randomizes a color based on a given spec
   */
  def getRandomColor(color: Color, spec: ColorSpec): Color = {
    val newSpec = ColorSpec(color, spec.lDeltaRange, spec.rgbDeltaRange, spec.aDeltaRange)
    newSpec.getColor(r)
  }
}
